package com.cqs.learning.spring.cloud.producer.aop;

import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.Tracer;
import com.alibaba.csp.sentinel.context.ContextUtil;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import lombok.extern.log4j.Log4j2;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.UndeclaredThrowableException;

import static com.cqs.learning.spring.cloud.producer.config.SentinelRulesConfig.GLOBAL_RESOURCE_NAME_KEY;

/**
 * @author lixw
 * @date created in 23:34 2020/4/1
 */
@Component
@Log4j2
@Aspect
public class FlowAspect {
    //开启黑白名单控制限流
    private final boolean authorityOn = true;

    @Pointcut("execution(public * com.cqs.learning.spring.cloud.producer.controller.*.*(..))")
    private void flowPointCut() {
    }


    @Around("flowPointCut()")
    public Object invoke(ProceedingJoinPoint pjp) throws BlockException {
        Signature signature = pjp.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        //选择暴露提供对外的接口 带有GetMapping，PostMapping等均注解上有@RequestMapping的声明
        Object annotation = method.getAnnotation(RequestMapping.class);
        if (annotation == null) {
            for (Annotation methodAnnotation : method.getAnnotations()) {
                Class<? extends Annotation> aClass = methodAnnotation.annotationType();
                Annotation[] annotations = aClass.getDeclaredAnnotations();
                for (Annotation annotation1 : annotations) {
                    if (annotation1 instanceof RequestMapping) {
                        annotation = annotation1;
                        break;
                    }
                }
                if (annotation != null) break;
            }
        }
        Object result = null;
        if (annotation != null) {
            //限流检测
            //获取到资源 初始化规则
            String resourceName = GLOBAL_RESOURCE_NAME_KEY;
            //开启黑白名单限速
            Object[] args = pjp.getArgs();
            String origin = "default_app";
            if (args != null && args.length > 0 && args[0] instanceof String) {
                origin = (String) args[0];
            }
//            log.info("调用源头:{}", origin);
            //白名单开启
            ContextUtil.enter(resourceName, origin);
            Entry entry = null;
            try {
                entry = SphU.entry(resourceName);
                try {
                    result = pjp.proceed(pjp.getArgs());
                } catch (Throwable throwable) {
                    if (throwable instanceof UndeclaredThrowableException) {
                        UndeclaredThrowableException exception = (UndeclaredThrowableException) throwable;
                        Throwable undeclaredThrowable = exception.getUndeclaredThrowable();
                        if (undeclaredThrowable instanceof DegradeException) {
                            //重新抛出异常 -- 这里不能处理异常，抛出也业务方处理
                            //new DegradeServiceImpl().cntFallback(undeclaredThrowable);
                            throw (DegradeException) undeclaredThrowable;
                        }
                    }
                    Tracer.trace(throwable);

                }
            } finally {
                if (entry != null) {
                    entry.exit();
                }
                ContextUtil.exit();
            }
        }
        return result;
    }
}
